package com.rich.paas.aop;

import com.alibaba.fastjson.JSON;
import com.rich.paas.annotation.NoLogin;
import com.rich.paas.constant.Constant;
import com.rich.paas.domain.Result;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 后台接口接口参数、权限验证
 *
 * @author chenhao
 * @version 1.0
 * @date 2018/7/25 下午4:07
 */
@Aspect
@Configuration
public class ControllerValidator {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());


    /**
     * 后台接口切面
     */
    @Pointcut("execution (* com.rich.paas.controller.ba..*.*(..))")
    public void baValidatorAspect() {
    }
    /**
     * 前端接口切面
     */
    @Pointcut("execution (* com.rich.paas.controller.front..*.*(..))")
    public void frontValidatorAspect() {
    }

    /**
     * 后台接口鉴权
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("baValidatorAspect()")
    public Object doBaAroundService(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method method = ms.getMethod();
        NoLogin noLogin = method.getAnnotation(NoLogin.class);
        if (noLogin == null) {
            //从session中获取用户信息
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            Object userInfo = request.getSession().getAttribute(Constant.LOGIN_BA_USER_INFO);
            if (userInfo == null) {
                return Result.noPermission();
            }
        }
        return this.proceed(joinPoint);
    }

    /**
     * 前端接口鉴权
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("frontValidatorAspect()")
    public Object doFrontBaAroundService(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method method = ms.getMethod();
        NoLogin noLogin = method.getAnnotation(NoLogin.class);
        if (noLogin == null) {
            //从session中获取用户信息
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            Object userInfo = request.getSession().getAttribute(Constant.LOGIN_FRONT_USER_INFO);
            if (userInfo == null) {
                return Result.noPermission();
            }
        }
        return this.proceed(joinPoint);
    }


    /**
     * 参数验证
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    private Object proceed(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] objects = joinPoint.getArgs();
        //获取类名称
        String targetName = joinPoint.getTarget().getClass().getSimpleName();
        //获取方法名称
        String methodName = joinPoint.getSignature().getName();
        List<Object> paramList = new ArrayList<>();
        for (Object object : objects) {
            if (!(object instanceof HttpServletRequest)) {
                paramList.add(object);
            }
        }
        logger.info("=======> HTTP START {}.{}，param:{}", targetName, methodName, JSON.toJSONString(paramList));
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        for (Object object : paramList) {
            if (object != null) {
                Set<ConstraintViolation<Object>> violations = validator.validate(object);
                if (!violations.isEmpty()) {
                    StringBuilder sb = new StringBuilder();
                    for (ConstraintViolation cv : violations) {
                        sb.append(cv.getPropertyPath());
                        sb.append(cv.getMessage());
                        sb.append(",");
                    }
                    return Result.failed(sb.toString().substring(0, sb.toString().length() - 1));
                }
            }
        }
        long start = System.currentTimeMillis();
        Object proceed = joinPoint.proceed();
        logger.info("<======= HTTP END {}.{} end, response:{}, time:{} ms", targetName, methodName, JSON.toJSONString(proceed), (System.currentTimeMillis() - start));
        return proceed;
    }

}
